package logger

import (
	"fmt"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"io"
	"os"
	"path/filepath"
	"time"
)

const (
	DefaultLevel      = zapcore.InfoLevel // 默认日志等级
	DefaultTimeLayout = time.RFC3339      // 默认数据的时间格式
)

type option struct {
	level          zapcore.Level
	fields         map[string]string
	file           io.Writer
	timeLayout     string
	disableConsole bool
}
type Option func(*option)

// WithDisableConsole 是否输出日志到终端
func WithDisableConsole() Option {
	return func(o *option) {
		o.disableConsole = true
	}
}

// WithField 添加 字段 到 日志
func WithField(key, value string) Option {
	return func(o *option) {
		o.fields[key] = value
	}
}

// WithTimeLayout 自定义 输出时间格式
func WithTimeLayout(time string) Option {
	return func(o *option) {
		o.timeLayout = time
	}
}

// WithFieldP 日志写入文件
func WithFieldP(file string) Option {
	dir := filepath.Dir(file)
	fmt.Println("WithFieldP file path", file)
	if err := os.MkdirAll(dir, 0766); err != nil {
		panic(err)
	}

	f, err := os.OpenFile(file, os.O_CREATE|os.O_APPEND|os.O_RDWR, 0766)

	if err != nil {
		panic(err)
	}

	return func(o *option) {
		o.file = zapcore.Lock(f)
	}
}

func NewJSONLogger(opts ...Option) (*zap.Logger, error) {

	opt := &option{
		level:  DefaultLevel,
		fields: make(map[string]string),
	}
	// 调用传递的配置函数（配置日志函数）
	for _, f := range opts {
		f(opt)
	}
	EncoderConfig := zapcore.EncoderConfig{
		MessageKey:     "msg",
		LevelKey:       "level",
		TimeKey:        "time",
		NameKey:        "logger",
		CallerKey:      "caller",
		FunctionKey:    "",
		StacktraceKey:  "stackTrace",
		SkipLineEnding: false,
		LineEnding:     zapcore.DefaultLineEnding,
		EncodeLevel:    zapcore.LowercaseLevelEncoder,
		EncodeTime: func(t time.Time, encoder zapcore.PrimitiveArrayEncoder) {
			encoder.AppendString(t.Format(DefaultTimeLayout))
		},
		EncodeDuration:      zapcore.MillisDurationEncoder,
		EncodeCaller:        zapcore.ShortCallerEncoder, // 全路径编码器
		EncodeName:          nil,
		NewReflectedEncoder: nil,
		ConsoleSeparator:    "----------------------------",
	}
	jsonEncoder := zapcore.NewJSONEncoder(EncoderConfig)

	// 低等级使用 info debug warn
	lowPriority := zap.LevelEnablerFunc(func(level zapcore.Level) bool {
		// 默认配置等级 info，输出日志等级 大于 info 小于 error
		return level >= opt.level && level < zapcore.ErrorLevel
	})

	// 高等级使用 error panic fatal
	heightPriority := zap.LevelEnablerFunc(func(level zapcore.Level) bool {
		return level >= opt.level && level >= zapcore.ErrorLevel
	})

	// lock for concurrent safe
	stdout := zapcore.Lock(os.Stdout)
	stderr := zapcore.Lock(os.Stderr)

	// NewTee创建一个Core，将日志条目复制到两个或多个底层Core中。
	core := zapcore.NewTee()

	if !opt.disableConsole { // 终端输出
		core = zapcore.NewTee(
			//NewCore creates a Core that writes logs to a WriteSyncer.
			zapcore.NewCore(jsonEncoder, zapcore.NewMultiWriteSyncer(stdout), lowPriority),
			zapcore.NewCore(jsonEncoder, zapcore.NewMultiWriteSyncer(stderr), heightPriority),
		)
	}

	// 如果配置 日志输出路径 将日志保存到 文件中
	if opt.file != nil {
		core = zapcore.NewTee(core,
			zapcore.NewCore(
				jsonEncoder,
				zapcore.AddSync(opt.file),
				zap.LevelEnablerFunc(func(level zapcore.Level) bool {
					return level >= opt.level
				}),
			),
		)
	}

	logger := zap.New(
		core,
		zap.AddCaller(),
		zap.ErrorOutput(stderr),
	)

	for key, value := range opt.fields {
		logger = logger.WithOptions(zap.Fields(zapcore.Field{Key: key, Type: zapcore.StringType, String: value}))
	}

	return logger, nil
}
